/*
 *   This file is part of ed_pvr.
 *
 *   ed_pvr is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   ed_pvr is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ed_pvr.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <memory.h>

#include "utils.h"
#include "bitstream.h"


#define BIT_GET_FILTER				0x80
#define BIT_PUT_FILTER				0x01

// ***********************
// ***********************
//
// Bitstream functions
//
// ***********************
// ***********************
BITSTREAM * bits_create(uint8_t * stream, uint32_t len)
{
	BITSTREAM * b = calloc(1, sizeof(BITSTREAM));

	b->stream = stream;
	b->len = len;
	b->bit_offset = 0;

	return b;
}

BITSTREAM * bits_create_put(uint8_t * stream, uint32_t len)
{
	BITSTREAM * b = bits_create(stream, len);
	memset(stream, 0, len);
	return b;
}

int8_t bits_reset(BITSTREAM * b)
{
	int8_t r = 0;
	if (b)
		b->bit_offset = 0;
	else
		r = -1;
	return r;
}


int8_t bits_peek_next_bit(BITSTREAM * b)
{
	int8_t r = bits_get_next_bit(b);

	b->bit_offset--;

	return r;
}

int8_t bits_get_next_bit(BITSTREAM * b)
{
	int8_t ret = -1;

	if (b && b->stream && (b->bit_offset < (b->len*BYTE_SIZE)))
	{
		uint32_t _off = b->bit_offset - ((b->bit_offset / BYTE_SIZE) * BYTE_SIZE);
		ret = ((b->stream[b->bit_offset++/BYTE_SIZE] & (BIT_GET_FILTER >> _off) ) >> (7 - _off));
	}
	return ret;
}

uint64_t bits_peek_bit_count(BITSTREAM * b, uint32_t num_of_bits, int8_t *rc)
{
	uint64_t r = bits_get_bit_count(b, num_of_bits, rc);

	if (*rc >= 0)
		b->bit_offset -= num_of_bits;
	return r;
}

uint64_t bits_get_bit_count(BITSTREAM * b, uint32_t num_of_bits, int8_t *rc)
{
	int8_t i;
	uint64_t ret = 0;

	for (i = (num_of_bits - 1); i >= 0; i--)
	{
		if ((*rc = bits_get_next_bit(b)) < 0)
			break;
		ret |= (*rc << i);
	}
	return ret;
}

uint64_t bits_get_ue_golomb(BITSTREAM * b, int8_t *rc)
{
	uint32_t zero_count = 0;
	uint64_t ret = 0;

	while ((*rc = bits_get_next_bit(b)) == 0)
		zero_count++;

	if (zero_count && *rc >= 1)
	{
		uint32_t i;
		ret = (1 << zero_count);

		for (i = zero_count; i > 0; i--)
		{
			if ((*rc = bits_get_next_bit(b)) != -1)
				ret |= (*rc << (i - 1));
			else
				break;
		}
		ret--;
	}
	return ret;
}

int64_t bits_get_se_golomb(BITSTREAM * b, int8_t *rc)
{
	int64_t ret = 0;
	uint64_t tmp = 0;

	tmp = bits_get_ue_golomb(b, rc);

	if (*rc >= 0)
		ret = pow(-1.0, (double)tmp + 1.0) * ceil((double)tmp / 2.0);

	return ret;
}


uint16_t bits_get_be16(BITSTREAM * b, int8_t *rc)
{
	uint16_t val;
	uint16_t tmp[2];

	tmp[0] = (uint16_t) bits_get_bit_count(b, 8, rc);
	tmp[1] = (uint16_t) bits_get_bit_count(b, 8, rc);
	val = (tmp[0] << 8) | tmp[1];
	return val;
}


uint32_t bits_get_be24(BITSTREAM * b, int8_t *rc)
{
	uint32_t val;
	uint32_t tmp[2];

	tmp[0] = bits_get_be16(b, rc);
	tmp[1] = (uint16_t) bits_get_bit_count(b, 8, rc);
	val = (tmp[0] << 16) | tmp[1];
	return val;
}

uint32_t bits_get_be32(BITSTREAM * b, int8_t *rc)
{
	uint32_t val;
	uint32_t tmp[2];

	tmp[0] = (uint32_t) bits_get_be16(b, rc);
	tmp[1] = (uint32_t) bits_get_be16(b, rc);
	val = (tmp[0] << 16) | tmp[1];
	return val;
}

void bits_align_8(BITSTREAM * b, int8_t *rc)
{
	if (b->bit_offset % 8)
		bits_get_bit_count(b, (((b->bit_offset / 8 ) + 1) * 8) - b->bit_offset, rc);
}



uint8_t bits_invert_byte(uint8_t b)
{
	uint8_t c = 0;
	c |= (0x80 & b) >> 7;
	c |= (0x40 & b) >> 5;
	c |= (0x20 & b) >> 3;
	c |= (0x10 & b) >> 1;

	c |= (0x8 & b) << 1;
	c |= (0x4 & b) << 3;
	c |= (0x2 & b) << 5;
	c |= (0x1 & b) << 7;

	return c;

}


#ifdef _MAIN_

int main(int argc, char *argv[])
{
	return 0;
}
#endif

